/*
 * SemLib.cpp
 *
 *  Created on: 2010-01-11
 *      Author: Maciej Skrzypkowski
 */

#include "SemLib.h"



SemLib::SemLib() {
	clientName = new char[32];
	char str[] = "C++ client :-) ";
	strcpy(clientName, str);

	srand (time (NULL));

	// losuj nazwę
	for (int i=15; i<31; ++i) {
		clientName[i] = rand() % 74 + 48;
	}
	clientName[31] = '\0';

	std::ostringstream oss;
	oss << "Client name: " << clientName;
	Log::instance()->info(oss.str());

}


SemLib::~SemLib() {

}


SemLib* SemLib::inst = 0;


SemLib* SemLib::instance() {
	if (!inst) {
		inst = new SemLib();
	}
	return inst;
}


bool SemLib::sendPing(TCPConnection* conn) {
	//Ping* msg = new Ping;
	InfoMessage* msg = new InfoMessage;

	msg->type = MSG_PING;
	if (!conn->send((uint8*)msg, sizeof(*msg))) {
		Log::instance()->error("Ping: polaczenie z serwerem zerwane");
		return false;
	}

	//Log::instance()->debug("Ping!");

	return true;
}


bool SemLib::send(TCPConnection* conn, char type, String semName) {
	void* msgPtr;
	uint size=0;

	switch (type) {
	case MSG_GREETING:
		ClientMessage msg;
		msg.type = type;
		strcpy(msg.clientName, clientName);
		size = sizeof(msg);
		msgPtr = &msg;
		break;
	case MSG_EXIT:
		InfoMessage infoMsg;
		infoMsg.type = type;
		size = sizeof(infoMsg);
		msgPtr = &infoMsg;
		break;
	case MSG_CREATE_SEM:
	case MSG_DELETE_SEM:
	case MSG_P_SEM:
	case MSG_V_SEM:
		SemaphoreMessage semMsg;
		semMsg.type = type;
		memset(semMsg.semName, 0, sizeof(semMsg.semName));
		strcpy(semMsg.semName, semName.c_str());
		size = sizeof(semMsg);
		msgPtr = &semMsg;
		break;
	}


	if (!conn->send((uint8*)msgPtr, size)) {
		Log::instance()->error("Polaczenie z serwerem zerwane przy wysłaniu wiadomości.");
		return false;
	}



	return true;
}


bool SemLib::connect(const char *serverName) {
	String s(serverName);
	uint i=0;
	String ip = "";
	String port = "";
	bool portFlag = false;

	if (connectionMap.find(s) != connectionMap.end()) {
		Log::instance()->warn("Klient jest już połączony z serwerem: "+s);
		return false;		// takie połączenie już istnieje
	}

	// parsowanie adresu i portu
	for (; i<s.length(); ++i) {
		if (s[i] == ':') {
			portFlag = true;
			continue;
		}
		if (!portFlag)
			ip += s[i];
		else
			port += s[i];
	}

	Log::instance()->info("Łączenie się z serwerem: "+ip+" na porcie: "+port);
	istringstream stream(port);
	int portInt;
	stream >> portInt;

	TCPConnection* conn = new TCPConnection();
	conn->setIPAddress(IPAddress(ip, portInt));
	conn->connect();

	if (conn->getState() != CONN_STATE_CONNECTED) {
		delete conn;
		Log::instance()->warn("Nie można połączyć się z serwerem: "+s);
		return false;
	} else {
		connectionMap[s] = conn;
	}

	if (!send(conn, MSG_GREETING, "")) {
		conn->disconnect();
		delete conn;
		String s(serverName);
		Log::instance()->warn("Wiadomość greeting do serwera " + s + " nie doszła!");
		return false;
	}

	Receiver* receiver = new Receiver(conn, this, serverName);
	receiver->start();
	receiversMap[s] = receiver;

	AnyMessage* rMsg = receiversMap[s]->getNewMsg();
	if (rMsg == NULL) {
		Log::instance()->warn("Nie można połączyć się z serwerem: "+s);
		return false;
	}
	if (rMsg->client.type == MSG_SUCCESS) {
		Log::instance()->info("Połączono!");
		return true;
	}
	else {
		Log::instance()->warn("Nie można połączyć się z serwerem: "+s);
		disconnect(serverName);
		return false;
	}

}


void SemLib::deleteServer(String serverName) {
	TCPConnection* conn = connectionMap[serverName];
	delete receiversMap[serverName];
	conn->disconnect();
	delete conn;
	connectionMap.erase(serverName);
}


bool SemLib::disconnect(const char *serverName) {
	String s(serverName);

	Log::instance()->info("Rozłączanie z serwerem: "+s);

	if (connectionMap.find(s) == connectionMap.end()) {
		Log::instance()->warn("Klient jest już rozłączony z serwerem: "+s);
		return false;
	}

	TCPConnection* conn = connectionMap[s];
	send(conn, MSG_EXIT, "");

	receiversMap[s]->stop();
	receiversMap[s]->terminate();
	delete receiversMap[s];

	conn->disconnect();
	delete conn;
	connectionMap.erase(s);
	return true;
}


void SemLib::createSemaphore(const char* semaphoreName, const char* serverName) {
	String s(serverName);
	String sem(semaphoreName);

	if (!checkConnection(s))
		return;

	TCPConnection* conn = connectionMap[s];

	if (!send(conn, MSG_CREATE_SEM, sem)) {
		disconnect(serverName);
		return;
	}

	AnyMessage* rMsg = receiversMap[s]->getNewMsg();
	if (rMsg == NULL) {
		Log::instance()->warn("Błąd utworzenia semafora " + sem + " !");
		return;
	}
	if (rMsg->client.type == MSG_SUCCESS)
		Log::instance()->info("Semafor '"+sem+"' został utworzony.");
	else
		Log::instance()->warn("Błąd utworzenia semafora " + sem + " !");

}


void SemLib::deleteSemaphore(const char* semaphoreName, const char* serverName) {
	String s(serverName);
	String sem(semaphoreName);

	if (!checkConnection(s))
		return;

	TCPConnection* conn = connectionMap[s];

	if (!send(conn, MSG_DELETE_SEM, sem)) {
		disconnect(serverName);
		return;
	}

	AnyMessage* rMsg = receiversMap[s]->getNewMsg();
	if (rMsg == NULL) {
		Log::instance()->warn("Błąd skasowania semafora " + sem + " !");
		return;
	}
	if (rMsg->client.type == MSG_SUCCESS)
		Log::instance()->info("Semafor '"+sem+"' został usunięty.");
	else
		Log::instance()->warn("Błąd skasowania semafora " + sem + " !");

}


// opuszczenie - P
void SemLib::pSemaphore(const char* semaphoreName, const char* serverName) {
	String s(serverName);
	String sem(semaphoreName);

	if (!checkConnection(s))
		return;

	TCPConnection* conn = connectionMap[s];

	if (!send(conn, MSG_P_SEM, sem)) {
		disconnect(serverName);
		return;
	}

	AnyMessage* rMsg = receiversMap[s]->getNewMsg();
	if (rMsg == NULL) {
		Log::instance()->warn("Błąd wykonania operacji P na semaforze " + sem + " !");
		return;
	}
	if (rMsg->client.type == MSG_SUCCESS)
		Log::instance()->info("Wykonano operację P na semaforze '"+sem+"'.");
	else
		Log::instance()->warn("Błąd wykonania operacji P na semaforze " + sem + " !");

}


// podniesienie - V
void SemLib::vSemaphore(const char* semaphoreName, const char* serverName) {
	String s(serverName);
	String sem(semaphoreName);

	if (!checkConnection(s))
		return;

	TCPConnection* conn = connectionMap[s];

	if (!send(conn, MSG_V_SEM, sem)) {
		disconnect(serverName);
		return;
	}

	AnyMessage* rMsg = receiversMap[s]->getNewMsg();
	if (rMsg == NULL) {
		Log::instance()->warn("Błąd wykonania operacji V na semaforze " + sem + " !");
		return;
	}
	if (rMsg->client.type == MSG_SUCCESS)
		Log::instance()->info("Wykonano operację V na semaforze '"+sem+"'.");
	else
		Log::instance()->warn("Błąd wykonania operacji V na semaforze " + sem + " !");

}

